﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using DimensionsHelper.Common.Contracts.Excel;
using DimensionsHelper.Common.Contracts.Excel.Options;
using DimensionsHelper.Common.TableFormatter;
using DimensionsHelper.Common.Utils;
using MsExcel = Microsoft.Office.Interop.Excel;

namespace DimensionsHelper.ExcelAddIn.Ms.Operator
{

    public class ExcelWorkbookOperator : IExcelWorkbookOperator
    {

        private bool _disposed;
        private string _fullName;
        private MsExcel.Application _excelApp;
        private Dictionary<string, ExcelWorksheetOperator> _worksheets;

        internal MsExcel.Workbook WorkbookInternal { get; private set; }

        private ExcelWorkbookOperator(MsExcel.Application excelApp, MsExcel.Workbook workbook)
        {
            _excelApp = excelApp;
            WorkbookInternal = workbook;
            Initialize();
        }


        public static IExcelWorkbookOperator FromApplication(MsExcel.Application excelApp, MsExcel.Workbook workbook)
        {
            return new ExcelWorkbookOperator(excelApp, workbook);
        }


        private void CheckInitialized()
        {
            if (!IsInitialized)
            {
                throw new Exception($"Excel尚未初始化，请先调用'{nameof(IExcelWorkbookOperator.Open)}'或'{nameof(IExcelWorkbookOperator.Create)}'方法创建或打开文件。");
            }
        }

        private void CheckDisposed()
        {
            if (_disposed)
            {
                throw new Exception("Excel对象已关闭，无法使用此对象打开或创建文件。");
            }

            Close();
        }

        private void Initialize()
        {
            if (WorkbookInternal == null)
            {
                throw new Exception("Excel对象未初始化，请先调用'Create'或'Open'方法创建或打开文件。");
            }

            if (_worksheets != null)
            {
                return;
            }

            _worksheets = new Dictionary<string, ExcelWorksheetOperator>(StringComparer.OrdinalIgnoreCase);
            foreach (MsExcel.Worksheet worksheet in WorkbookInternal.Worksheets)
            {
                var excelWorksheetOperator = new ExcelWorksheetOperator(this, worksheet);
                _worksheets.Add(worksheet.Name, excelWorksheetOperator);
            }

            IsInitialized = true;
        }


        public bool IsInitialized { get; private set; }

        public string FileName => _fullName ?? string.Empty;

        public FontStyleOptions HyperlinkStyleOptions { get; set; } = TableFormatterDefaultValues.DefaultHyperlinkStyleOptions;

        public FontStyleOptions GlobalFontStyleOptions { get; set; } = new FontStyleOptions();

        public IList<IExcelWorksheetOperator> Worksheets
        {
            get
            {
                CheckInitialized();
                Initialize();
                return _worksheets.Values.ToList<IExcelWorksheetOperator>();
            }
        }

        public void Close(bool withoutSave = true)
        {
            if (!IsInitialized || _disposed)
            {
                return;
            }

            _worksheets?.Clear();
           
            if (WorkbookInternal != null)
            {
                WorkbookInternal?.Close(!withoutSave);
                Marshal.ReleaseComObject(WorkbookInternal);
            }

            WorkbookInternal = null;

            if (_excelApp != null)
            {
                _excelApp.Quit();
                Marshal.ReleaseComObject(_excelApp);
            }

            _excelApp = null;
            _disposed = true;
            IsInitialized = false;
        }

        public void Create(string fileName)
        {
            if (IsInitialized)
            {
                throw new Exception("Excel对象已打开，无法使用此对象创建文件。");
            }

            if (File.Exists(fileName))
            {
                File.Delete(fileName);
            }

            _excelApp = new MsExcel.Application();
            WorkbookInternal = _excelApp.Workbooks.Add();
            WorkbookInternal.SaveAs(fileName);
            Initialize();
        }

        public void DeleteWorksheet(string fileName)
        {
            CheckInitialized();

            if (_worksheets?.Remove(fileName, out var excelWorksheet) == true)
            {
                try
                {
                    _excelApp.DisplayAlerts = false;
                    excelWorksheet.WorksheetInternal.Delete();

                    if (excelWorksheet.WorksheetInternal != null)
                    {
                        Marshal.ReleaseComObject(excelWorksheet.WorksheetInternal);
                    }
                }
                finally
                {
                    _excelApp.DisplayAlerts = true;
                }
            }
        }

        public bool ContainsWorksheet(string sheetName)
        {
            CheckInitialized();
            Initialize();
            return _worksheets.ContainsKey(sheetName);
        }


        public IExcelWorksheetOperator GetWorksheet(string sheetName)
        {
            CheckInitialized();
            Initialize();

            if (_worksheets.TryGetValue(sheetName, out var worksheetOperator))
            {
                return worksheetOperator;
            }

            var msWorksheet = (MsExcel.Worksheet)WorkbookInternal.Worksheets.Add();
            msWorksheet.Name = sheetName;
            worksheetOperator = new ExcelWorksheetOperator(this, msWorksheet);
            _worksheets.Add(sheetName, worksheetOperator);
            return worksheetOperator;
        }

        public void Open(string fileName)
        {
            CheckDisposed();

            if (!File.Exists(fileName))
            {
                throw new Exception($"{fileName} 不存在.");
            }

            _fullName = fileName;
            _excelApp = new MsExcel.Application();
            WorkbookInternal = _excelApp.Workbooks.Open(fileName);
            Initialize();
        }

        public void Save(string destination = "")
        {
            CheckInitialized();

            if (string.IsNullOrEmpty(destination))
            {
                WorkbookInternal?.Save();
            }
            else
            {
                WorkbookInternal?.SaveAs(destination);
            }
        }
    }
}